package middleware

import (
	"errors"
	"ginblog/config"
	"ginblog/enum"
	"ginblog/resp"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"strings"
	"time"
)

var r resp.Resp

type JWT struct {
	JwtKey []byte
}

// NewJWT 读取配置文件中的密钥，生成对应的byte切片
func NewJWT() *JWT {
	return &JWT{
		[]byte(config.JwtKey),
	}
}

// MyClaims 定义结构体，用于获取用户的账户信息
type MyClaims struct {
	Username string `json:"username"`
	// 继承自带的结构体
	jwt.StandardClaims
}

// 定义错误
var (
	TokenExpired     = errors.New("token已过期,请重新登录")
	TokenNotValidYet = errors.New("token无效,请重新登录")
	TokenMalformed   = errors.New("token不正确,请重新登录")
	TokenInvalid     = errors.New("这不是一个token,请重新登录")
)

// SetToken 生成token，返回值1：所生成的Token，返回值2：是否生成成功
func (j *JWT) SetToken(username string) (string, enum.Code) {
	// 设置有效期
	expireTime := time.Now().Add(time.Hour * 24)

	// 传递参数到结构体中
	SetClaims := MyClaims{
		Username: username,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: expireTime.Unix(), // 有效时间，这里需要传递时间戳
			Issuer:    "xiongda",         // 签发人
		},
	}

	// 生成token
	// 参数1：签发者方法 SigningMethodHS256:标准方法，返回的是token的指针
	reqClaim := jwt.NewWithClaims(jwt.SigningMethodHS256, SetClaims)

	// 将JWT的key加盐后转化成string
	token, err := reqClaim.SignedString(NewJWT().JwtKey)

	if err != nil {
		return "", enum.ERROR
	}
	return token, enum.SUCCESS
}

// ParserToken 验证token
func (j *JWT) ParserToken(tokenString string) (*MyClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
		return j.JwtKey, nil
	})

	if err != nil {
		if ve, ok := err.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
				return nil, TokenMalformed
			} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
				return nil, TokenExpired
			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
				return nil, TokenNotValidYet
			} else {
				return nil, TokenInvalid
			}
		}
	}

	if token != nil {
		if claims, ok := token.Claims.(*MyClaims); ok && token.Valid {
			return claims, nil
		}
		return nil, TokenInvalid
	}

	return nil, TokenInvalid
}

// JwtToken jwt中间件：路由的控制token验证
func JwtToken() gin.HandlerFunc {
	return func(c *gin.Context) {
		var code enum.Code
		// 定义规范
		tokenHeader := c.Request.Header.Get("Authorization")
		// 判断是否有token
		if tokenHeader == "" {
			code = enum.ErrorTokenNotExist
			r.RespData(code, code.String(), nil, c)

			// Abort 在被调用的函数中阻止挂起函数。
			// 注意这将不会停止当前的函数。例如，
			// 你有一个验证当前的请求是否是认证过的 Authorization
			// 中间件。如果验证失败(例如，密码不匹配)，调用 Abort
			// 以确保这个请求的其他函数不会被调用
			c.Abort()
			return
		}

		// 验证token的格式是否正确
		checkToken := strings.Split(tokenHeader, " ")
		if len(checkToken) == 0 {
			code = enum.ErrorTokenTypeWrong
			r.RespData(code, code.String(), nil, c)
			c.Abort()
			return
		}

		if len(checkToken) != 2 || checkToken[0] != "Bearer" {
			code = enum.ErrorTokenTypeWrong
			r.RespData(code, code.String(), nil, c)
			c.Abort()
			return
		}

		j := NewJWT()
		// 解析token
		claims, err := j.ParserToken(checkToken[1])
		if err != nil {
			if err == TokenExpired {
				r.RespData(enum.ERROR, "token授权已过期,请重新登录", nil, c)
				c.Abort()
				return
			}
			// 其他错误
			r.RespData(enum.ERROR, err.Error(), nil, c)
			c.Abort()
			return
		}

		c.Set("username", claims)
		c.Next()
	}
}
